//
//  Engine.m
//  Void Dead
//
//  Created by Sidney Just on 02.07.09.
//  Copyright 2009 by Sidney Just
//  This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; 
//  either version 3 of the License, or (at your option) any later version.
//  
//  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
//  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//  

#import "Engine.h"

#define USE_DEPTH_BUFFER 0

#pragma mark -
#pragma mark implentation

@implementation VD_Engine

@synthesize context;
@synthesize viewRenderbuffer, viewFramebuffer;
@synthesize animationInterval;

@synthesize timeStep, skyColor;
@synthesize backingWidth, backingHeight;
@synthesize texManager, panManager, colManager, lblManager, contentManager;
@synthesize FPSLabelDisplayMode, visiblePolygons, drawCalls, verticesCount;


// You must implement this method
+ (Class)layerClass {
    return [CAEAGLLayer class];
}

- (BOOL)setupView {
	
	FPSLabelDisplayMode = kDisplayNot;
	
	skyColor = [[VD_Color alloc] init];
	col_set(skyColor, color(0, 0, 0.5));
	
	// Get the layer
	CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
	
	eaglLayer.opaque = YES;
	eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
									[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
	
	context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
	
	if (!context || ![EAGLContext setCurrentContext:context]) {
		[self release];
		return NO;
	}
	
	animationInterval = 1.0 / 60.0;
	
	targets = 0;
	
	CCSelector = (SEL *)malloc(sizeof(SEL));
	CCTarget = (id *)malloc(sizeof(id));
	
	waits = 0;
	
	CCWaitSelector = (SEL *)malloc(sizeof(SEL));
	CCWaitTarget = (id *)malloc(sizeof(id));
	
	initHandleManager();
	texManager = [[VD_TextureManager alloc] init];
	panManager = [[VD_PanelManager alloc] initWithEngine:self];
	colManager = [[VD_CollisionManager alloc] initWithEngine:self];
	lblManager = [[VD_labelManager alloc] initWithEngine:self];
	
	contentManager = [[VD_ContentManager alloc] initWithEngine:self];
	
	fpsLabel = [lblManager createLabel];
	debugLabel = [lblManager createLabel];
	
	[animationTimer invalidate];
	
	return YES;
}

//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder {
    if(self = [super initWithCoder:coder]) {
		
		if([self setupView] == NO)
			return nil;
    }
	
    return self;
}

- (id)initWithFrame:(CGRect)frame {
	if(self = [super initWithFrame:frame])
	{
		self.frame = frame;
		if([self setupView] == NO)
			return nil;
	}
	
	return self;
}

- (void)scheduleRenderCallAt:(id)target withSelector:(SEL)selector {
	targets += 1;
	
	CCSelector = (SEL *)realloc(CCSelector, targets*sizeof(SEL));
	CCTarget = (id *)realloc(CCTarget, targets*sizeof(id));
	
	CCTarget[targets-1] = target;
	CCSelector[targets-1] = selector;
}

- (void)deleteRenderCallAt:(id)target andSelector:(SEL)selector {
	if(targets > 0)
	{
		for(int i=0; i<targets; i++)
		{
			if(target == CCTarget[i] && selector == CCSelector[i])
			{
				for(int j=i; j<targets; j++)
				{
					CCTarget[j] = CCTarget[j+1];
					CCSelector[j] = CCSelector[j+1];
				}
				
				targets -= 1;
				
				CCSelector = (SEL *)realloc(CCSelector, targets*sizeof(SEL));
				CCTarget = (id *)realloc(CCTarget, targets*sizeof(id));
				
				break;
			}
		}
	}
}

- (void)waitOneFrame:(id)target forSelector:(SEL)selector {
	waits += 1;
	
	CCWaitSelector = (SEL *)realloc(CCWaitSelector, waits*sizeof(SEL));
	CCWaitTarget = (id *)realloc(CCWaitTarget, waits*sizeof(id));
	
	CCWaitTarget[waits-1] = target;
	CCWaitSelector[waits-1] = selector;
}


- (void)drawView {
	visiblePolygons = 0;
	drawCalls = 0;
	verticesCount = 0;
	
    [EAGLContext setCurrentContext:context];
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
	
    glViewport(0, 0, backingWidth, backingHeight);
	
	glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
	
	glClearColor(skyColor.red, skyColor.green, skyColor.blue, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	// Panel
	[panManager drawAll];
	
	[lblManager drawAll];
	
	// Execeute the renderCalls
	if(forcedRendering == NO)
	{
		if(targets > 0)
		{
			for(int i=0; i<targets; i++)
			{
				[CCTarget[i] performSelector:CCSelector[i]];
			}
		}
		
		// Execute the waits
		if(waits > 0)
		{
			for(int i=0; i<waits; i++)
			{
				[CCWaitTarget[i] performSelector:CCWaitSelector[i]];
			}
			
			free(CCWaitTarget);
			free(CCWaitSelector);
			
			waits = 0;
			
			CCWaitSelector = (SEL *)malloc(sizeof(SEL));
			CCWaitTarget = (id *)malloc(sizeof(id));
		}
	}
	
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
	
	if(FPSLabelDisplayMode != kDisplayNot)
	{
		fpsLabel.visible = YES;
		debugLabel.visible = YES;
		
		[fpsLabel setString:[NSString stringWithFormat:@"FPS: %i", (int)(1/timeStep)] fontName:@"Arial" fontSize:13];
		[debugLabel setString:[NSString stringWithFormat:@"Polyogns: %i | Draw calls: %i | Vertices: %i", visiblePolygons, drawCalls, verticesCount] fontName:@"Arial" fontSize:13];
		
		if(FPSLabelDisplayMode == kDisplayUpLeft)
		{
			fpsLabel.pos_x = 5;
			fpsLabel.pos_y = 5;
			
			debugLabel.pos_x = 5;
			debugLabel.pos_y = 5+fpsLabel.size_y;
		} else if(FPSLabelDisplayMode == kDisplayUpRight)
		{
			fpsLabel.pos_x = backingWidth-fpsLabel.size_x-5;
			fpsLabel.pos_y = 5;
			
			debugLabel.pos_x = backingWidth-debugLabel.size_x-5;
			debugLabel.pos_y = 5+fpsLabel.size_y;
		} else if(FPSLabelDisplayMode == kDisplayDownRight)
		{
			fpsLabel.pos_x = backingWidth-fpsLabel.size_x-5;
			fpsLabel.pos_y = backingHeight-fpsLabel.size_y;
			fpsLabel.pos_y -= ([[UIApplication sharedApplication] isStatusBarHidden]) ? 5 : 25;
			
			debugLabel.pos_x = backingWidth-debugLabel.size_x-5;
			debugLabel.pos_y = fpsLabel.pos_y-fpsLabel.size_y-5;
			
		} else if(FPSLabelDisplayMode == kDisplayDownLeft)
		{
			fpsLabel.pos_x = 5;
			fpsLabel.pos_y = backingHeight-fpsLabel.size_y;
			fpsLabel.pos_y -= ([[UIApplication sharedApplication] isStatusBarHidden]) ? 5 : 25;
			
			debugLabel.pos_x = 5;
			debugLabel.pos_y = fpsLabel.pos_y-fpsLabel.size_y-5;
		}
		
	} else {
		fpsLabel.visible = NO;
	}
	
	if(forcedRendering == NO)
	{
		static NSTimeInterval lastDrawTime;
		if(lastDrawTime)
		{
			timeStep = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
		}
		lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
	}
}


- (void)forceRendering {
	BOOL temp = forcedRendering;
	forcedRendering = YES;
	
	[self drawView];
	
	forcedRendering = temp;
}

- (void)layoutSubviews {
    [EAGLContext setCurrentContext:context];
    [self destroyFramebuffer];
    [self createFramebuffer];
    [self drawView];
}


- (BOOL)createFramebuffer {
    
    glGenFramebuffersOES(1, &viewFramebuffer);
    glGenRenderbuffersOES(1, &viewRenderbuffer);
    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
    
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
    
    if (USE_DEPTH_BUFFER) {
        glGenRenderbuffersOES(1, &depthRenderbuffer);
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
        glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
    }
    
    if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
        return NO;
    }
    
    return YES;
}


- (void)destroyFramebuffer {
    
    glDeleteFramebuffersOES(1, &viewFramebuffer);
    viewFramebuffer = 0;
    glDeleteRenderbuffersOES(1, &viewRenderbuffer);
    viewRenderbuffer = 0;
    
    if(depthRenderbuffer) {
        glDeleteRenderbuffersOES(1, &depthRenderbuffer);
        depthRenderbuffer = 0;
    }
}



- (void)startAnimation {
	forcedRendering = NO;
    animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
}

- (void)stopAnimation {
	[animationTimer invalidate];
    animationTimer = nil;
}


- (void)setAnimationTimer:(NSTimer *)newTimer {
    [animationTimer invalidate];
    animationTimer = newTimer;
}


- (void)setAnimationInterval:(NSTimeInterval)interval {
    
    animationInterval = interval;
    if (animationTimer) {
        [self stopAnimation];
        [self startAnimation];
    }
}


- (void)dealloc {
    [skyColor release];
	
	free(CCTarget);
	free(CCSelector);
	
	[texManager release];
	[panManager release];
	[colManager release];
	[lblManager release];
	[contentManager release];
	
	[fpsLabel release];
	
    [self stopAnimation];
	removeHandleManager();
    
    if ([EAGLContext currentContext] == context) {
        [EAGLContext setCurrentContext:nil];
    }
    
    [context release];  
    [super dealloc];
}

@end
